#include <iostream>
#include <string>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>

#include "thread_pool.hpp"

using namespace std;

const uint16_t default_port = 8080;
const string default_ip = "0.0.0.0";
const int back_log_level = 5;
const size_t default_buffer_size = 128;

class server;

struct socket_info {
    socket_info(int fd, server *sp)
        : connect_sockfd(fd), srp(sp) {
    }
    int connect_sockfd;
    server *srp;
};

class server {
public:
    server(const uint16_t port = default_port, const string &ip = default_ip) 
        : _port(port), _ip(ip) {
        _thread_p = thread_pool<task>::get_instance();
    }

    void init() {
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_listen_sockfd == -1) {
            perror("create socket err");
            exit(errno);
        }

        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        if(inet_pton(AF_INET, _ip.c_str(), &local.sin_addr) != 1) {
            perror("inet_pton err");
            exit(errno);
        }

        if(bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local)) == -1) {
            perror("bind err");
            exit(errno);
        }

        //tcp套接字通信前需要先建立连接
        //通过监听，一旦有客户端来就与其建立连接然后通信
        if(listen(_listen_sockfd, back_log_level) == -1) {
            perror("listen err");
            exit(errno);
        }
        cout << "[server is listening...]" << endl;
    }

    // static void *server_client(void *args) {
    //     直接设置为线程分离，不要让主线程等待新线程退出
    //     pthread_detach(pthread_self());
    //     scoket_info *t = static_cast<scoket_info *>(args);
    //     char in_buff[default_buffer_size];
    //     while(true){
    //         使用文件接口操作套接字
    //         接收客户端发来的数据
    //         ssize_t n = read(t->connect_sockfd, in_buff, sizeof(in_buff) - 1);
    //         if(n <= 0) {
    //             if(n == 0) {
    //                 cout << "end of file, client close" << endl;
    //                 break;
    //             }
    //             else {
    //                 perror("read err");
    //                 exit(errno);
    //             }
    //         }
    //         in_buff[n] = '\0';
    //         cout << "[server get a msg]# " << in_buff << endl;
    //         简单处理再把数据返回给客户端
    //         string echo_string = "[echo_string]# ";
    //         echo_string += in_buff;
    //         n = write(t->connect_sockfd, echo_string.c_str(), echo_string.size());
    //         if(n == -1) {
    //             perror("write err");
    //             exit(errno);
    //         }
    //     }
    //     delete t;
    //     return nullptr;
    // }

    // void server_client(int connect_sockfd) {
    //     char in_buff[default_buffer_size];
    //     while(true){
    //         // 使用文件接口操作套接字
    //         // 接收客户端发来的数据
    //         ssize_t n = read(connect_sockfd, in_buff, sizeof(in_buff) - 1);
    //         if(n <= 0) {
    //             if(n == 0) {
    //                 cout << "end of file, client close" << endl;
    //                 break;
    //             }
    //             else {
    //                 perror("read err");
    //                 exit(errno);
    //             }
    //         }
    //         in_buff[n] = '\0';
    //         cout << "[server get a msg]# " << in_buff << endl;
    //         // 简单处理再把数据返回给客户端
    //         string echo_string = "[echo_string]# ";
    //         echo_string += in_buff;
    //         n = write(connect_sockfd, echo_string.c_str(), echo_string.size());
    //         if(n == -1) {
    //             perror("write err");
    //             exit(errno);
    //         }
    //     }
    // }
    
    // static void *thread_start(void *arg) {
    //     pthread_detach(pthread_self());
    //     socket_info *info = static_cast<socket_info *>(arg);
    //     info->srp->server_client(info->connect_sockfd);
    //     delete info;
    //     return nullptr;
    // }

    void run() {
        while(true) {
            // 与客户端建立连接
            struct sockaddr_in client_addr;
            socklen_t len = sizeof(client_addr);
            int connect_sockfd = accept(_listen_sockfd, (struct sockaddr *)&client_addr, &len);
            if(connect_sockfd == -1) {
                cout << "accept err, continue accept..." << endl;
                continue;
            }
            char ip_buff[16];
            cout << "[connect success client_addr: " << inet_ntop(AF_INET, &client_addr.sin_addr, ip_buff, sizeof(ip_buff)) 
                 << " client port: " << ntohs(client_addr.sin_port) << "]" << endl;
            // 后续客户端与服务器通过这个新套接字进行通信

            // version 1单进程
            // 只允许有一个客户端进行连接通信
            //server_client(connect_sockfd);

            //version 2多进程
            // pid_t pid = fork();
            // if(pid <= 0) {
            //     if(pid == -1) {
            //         perror("fork err");
            //         exit(errno);
            //     }
                // 子进程
                // 由于子进程会共享父进程的文件描述符表
                // 所以对于子进程来说，它只需要负责处理已连接套接字
                // 不需要监听套接字，所以子进程要关闭监听套接字的文件描述符
                // close(_listen_sockfd);
                // 子进程创建出孙子进程后直接退
                // 然后waitpid直接返回连接下一个任务，让孙子进程与客户端通信
            //     if(fork() > 0) {
            //         exit(0);
            //     }     
            //     server_client(connect_sockfd);
            //     close(connect_sockfd);
            //     exit(0);
            // }
            // 父进程
            // 由于子进程对连接套接字进行管理
            // 父进程就不需要这个连接套接字了
            // 所以关闭连接套接字
            // close(connect_sockfd);

            // 回收子进程防止僵尸进程
            // 但默认等待方式是阻塞，所以和单进程问题相似
            // 如果一个客户端一直不退，其它客户端也只能等待
            // 所以需要特殊处理
            // 子进程退出会发送信号，所以可以选择signal忽略
            // 这里使用一个技巧
            // 让子进程在创建出一个子进程，即孙子进程
            // 然后子进程直接退出，这样waitpid会立刻回收成功返回不会阻塞
            // 让孙子进程去执行服务逻辑，由于父进程先比子进程退出
            // 子进程会变成孤儿进程然后由操作系统领养，后续回收的工作系统会做，就不需要关心了
            // pid_t rid = waitpid(pid, nullptr, 0);
            // if(rid == -1) {
            //     perror("waitpid err");
            //     exit(errno);
            // }

            // version 3多线程
            // 由于创建进程的工作是比较重的
            // 所以上面多进程的做法在面临多并发的场景下效率是很低的
            // 所以选择创建多线程来处理客户端的请求
            // pthread_t tid;
            // socket_info *ti = new socket_info(connect_sockfd, this);
            // pthread_create(&tid, nullptr, thread_start, ti);

            // version 4线程池
            // 可以在服务器一启动时就创建出一批线程，也就是利用线程池
            // 这种做法相较于动态创建线程效率又要高出一些

            _thread_p->push(task(connect_sockfd));
        }
    }

private:
    int _listen_sockfd;
    uint16_t _port;
    string _ip;
    thread_pool<task> *_thread_p;
};